package mybitcoin

import (
	"crypto/sha256"
	"time"
	"bytes"
	"encoding/binary"
	"log"
	"encoding/gob"
)

type Block struct {
	//版本号
	Version uint64
	//上一区块的hash值
	PrevHash []byte
	//梅克尔根
	MerkleRoot []byte
	//时间戳
	TimeStamp uint64
	//Nonce值
	Nonce uint64
	//困难值
	Difficulty uint64
	//当前区块的hash值(暂时放入)
	CurrentHash []byte
	//区块数据（暂时放入）
	Data []byte
}

/*
1.创建区块
 */
func NewBlock(data string, prevHash []byte) *Block {
	block := Block{
		Version:    00,
		PrevHash:   prevHash,
		MerkleRoot: []byte{},
		TimeStamp:  uint64(time.Now().Unix()),
		//Nonce:      0,
		Difficulty: 0,
		//CurrentHash: []byte{},
		Data: []byte(data),
	}
	//block.SetHash()
	proofOfWork := NewProofOfWork(block)
	//不停的计算，得到奖励
	hash, nonce := proofOfWork.run()
	block.CurrentHash=hash
	block.Nonce=nonce
	return &block
}

/*
2.计算hash(转移到pow包中，因为这是由挖矿产生的，舍弃)
 */
func (block *Block) SetHash() {
	//拼接，整个结构体的hash
	var blockByteInfo []byte //存储拼接好的数据，最后作为sha256函数的参数
	//1. 拼接当前区块的数据
	/*
	blockByteInfo = append(blockByteInfo, block.PrevHash...)
	blockByteInfo = append(blockByteInfo, block.Data...)
	blockByteInfo = append(blockByteInfo, block.MerkleRoot...)
	blockByteInfo = append(blockByteInfo, uint64ToByte(block.Version)...)
	blockByteInfo = append(blockByteInfo, uint64ToByte(block.TimeStamp)...)
	blockByteInfo = append(blockByteInfo, uint64ToByte(block.Difficulty)...)
	blockByteInfo = append(blockByteInfo, uint64ToByte(block.Nonce)...)
	*/

	tmp := [][]byte{
		block.PrevHash,
		block.Data,
		block.MerkleRoot,
		Uint64ToByte(block.Version),
		Uint64ToByte(block.TimeStamp),
		Uint64ToByte(block.Difficulty),
		Uint64ToByte(block.Nonce)}

	blockByteInfo = bytes.Join(tmp, []byte(""))

	hash := sha256.Sum256(blockByteInfo)
	block.CurrentHash = hash[:]

}

/*
辅助函数uint转成【】byte
 */
func Uint64ToByte(i uint64) []byte {
	var buffer bytes.Buffer
	err:=binary.Write(&buffer, binary.BigEndian, i)
	if err!=nil{
		log.Panic("binary.Write...err")
	}

	return buffer.Bytes()
}

//数据库只能存【】byte ，需要把block转成
func (block *Block) Serialize() []byte{
	var buffer bytes.Buffer
	encoder := gob.NewEncoder(&buffer)
	err := encoder.Encode(&block)
	if err!=nil{
		log.Panic("encoder err")
	}
	return buffer.Bytes()

}
/*
反序列化
 */
func DeSerialize(data []byte)  Block{
	var block Block
	decoder := gob.NewDecoder(bytes.NewReader(data))
	err := decoder.Decode(&block)
	if err!=nil{
		log.Panic("decoder err")
	}
	return block
}
